//-----------------------------------------------------------------------------
// DatePreference
//-----------------------------------------------------------------------------

package com.tiktok.consumerapp.preference;

//-----------------------------------------------------------------------------
// imports
//-----------------------------------------------------------------------------

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.DatePicker;

import com.tiktok.consumerapp.R;

//-----------------------------------------------------------------------------
// class implementation
//-----------------------------------------------------------------------------

public class DatePreference extends DialogPreference
                            implements DatePicker.OnDateChangedListener

{
    private String dateString;
    private String changedValueCanBeNull;
    private DatePicker datePicker;

    //-------------------------------------------------------------------------

    public DatePreference(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
    }

    //-------------------------------------------------------------------------

    public DatePreference(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }

    //-------------------------------------------------------------------------

    /**
     * Produces a DatePicker set to the date produced by {@link #getDate()}. When
     * overriding be sure to call the super.
     *
     * @return a DatePicker with the date set
     */
    @Override
    protected View onCreateDialogView()
    {
        // inflate layout
        LayoutInflater inflator =
            (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View view = inflator.inflate(R.layout.date_picker_dialog, null);

        // configure date picker
        this.datePicker = (DatePicker)view.findViewById(R.id.datePicker);
        Calendar calendar = getDate();
        datePicker.init(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH),
            calendar.get(Calendar.DAY_OF_MONTH), this);
        return view;
    }

    //-------------------------------------------------------------------------

    /**
     * Produces the date used for the date picker. If the user has not selected a
     * date, produces the default from the XML's android:defaultValue. If the
     * default is not set in the XML or if the XML's default is invalid it uses
     * the value produced by {@link #defaultCalendar()}.
     *
     * @return the Calendar for the date picker
     */
    public Calendar getDate()
    {
        try {
            Date date         = formatter().parse(this.dateString);
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(date);
            return calendar;
        } catch (java.text.ParseException e) {
            return defaultCalendar();
        }
    }

    //-------------------------------------------------------------------------

    /**
     * Set the selected date to the specified string.
     *
     * @param dateString
     *          The date, represented as a string, in the format specified by
     *          {@link #formatter()}.
     */
    public void setDate(String dateString)
    {
        this.dateString = dateString;
    }

    //-------------------------------------------------------------------------

    /**
     * Produces the date formatter used for dates in the XML. The default is yyyy.MM.dd.
     * Override this to change that.
     *
     * @return the SimpleDateFormat used for XML dates
     */
    public static SimpleDateFormat formatter()
    {
        return new SimpleDateFormat("yyyy.MM.dd");
    }

    //-------------------------------------------------------------------------

    /**
     * Produces the date formatter used for showing the date in the summary. The default is MMMM dd, yyyy.
     * Override this to change it.
     *
     * @return the SimpleDateFormat used for summary dates
     */
    public static SimpleDateFormat summaryFormatter()
    {
        return new SimpleDateFormat("MMMM dd, yyyy");
    }

    //-------------------------------------------------------------------------

    @Override
    protected Object onGetDefaultValue(TypedArray array, int index)
    {
        return array.getString(index);
    }

    //-------------------------------------------------------------------------

    /**
     * Called when the date picker is shown or restored. If it's a restore it gets
     * the persisted value, otherwise it persists the value.
     */
    @Override
    protected void onSetInitialValue(boolean restoreValue, Object def)
    {
        if (restoreValue) {
            this.dateString = getPersistedString(defaultValue());
            setTheDate(this.dateString);
        } else {
            boolean wasNull = this.dateString == null;
            setDate((String) def);
            if (!wasNull) persistDate(this.dateString);
        }
    }

    //-------------------------------------------------------------------------

    /**
     * Called when Android pauses the activity.
     */
    @Override
    protected Parcelable onSaveInstanceState() {
        if (isPersistent()) {
            return super.onSaveInstanceState();
        } else {
            return new SavedState(super.onSaveInstanceState());
        }
    }

    //-------------------------------------------------------------------------

    /**
     * Called when Android restores the activity.
     */
    @Override
    protected void onRestoreInstanceState(Parcelable state)
    {
        if (state == null || !state.getClass().equals(SavedState.class)) {
            super.onRestoreInstanceState(state);
            setTheDate(defaultValue());
        } else {
            SavedState s = (SavedState) state;
            super.onRestoreInstanceState(s.getSuperState());
            setTheDate(s.dateValue);
        }
    }

    //-------------------------------------------------------------------------

    /**
     * Called when the user changes the date.
     */
    public void onDateChanged(DatePicker view, int year, int month, int day)
    {
        Calendar selected          = new GregorianCalendar(year, month, day);
        this.changedValueCanBeNull = formatter().format(selected.getTime());
    }

    //-------------------------------------------------------------------------

    /**
     * Called when the dialog is closed. If the close was by pressing "OK" it
     * saves the value.
     */
    @Override
    protected void onDialogClosed(boolean shouldSave)
    {
        if (shouldSave && (this.changedValueCanBeNull != null)) {
            setTheDate(this.changedValueCanBeNull);
            this.changedValueCanBeNull = null;
        }
    }

    //-------------------------------------------------------------------------

    private void setTheDate(String string)
    {
        setDate(string);
        persistDate(string);
    }

    //-------------------------------------------------------------------------

    private void persistDate(String s)
    {
        persistString(s);
        setSummary(summaryFormatter().format(getDate().getTime()));
    }

    //-------------------------------------------------------------------------

    /**
     * The default date to use when the XML does not set it or the XML has an
     * error.
     *
     * @return the Calendar set to the default date
     */
    public static Calendar defaultCalendar()
    {
        return new GregorianCalendar(1970, 0, 1);
    }

    //-------------------------------------------------------------------------

    /**
     * The defaultCalendar() as a string using the {@link #formatter()}.
     *
     * @return a String representation of the default date
     */
    public static String defaultCalendarString()
    {
        return formatter().format(defaultCalendar().getTime());
    }

    //-------------------------------------------------------------------------

    private String defaultValue()
    {
        return defaultCalendarString();
    }

    //-------------------------------------------------------------------------

    /**
     * Called whenever the user clicks on a button. Invokes {@link #onDateChanged(DatePicker, int, int, int)}
     * and {@link #onDialogClosed(boolean)}. Be sure to call the super when overriding.
     */
    @Override
    public void onClick(DialogInterface dialog, int which)
    {
        super.onClick(dialog, which);
        datePicker.clearFocus();
        onDateChanged(datePicker, datePicker.getYear(), datePicker.getMonth(),
            datePicker.getDayOfMonth());
        onDialogClosed(which == DialogInterface.BUTTON_POSITIVE);
    }

    //-------------------------------------------------------------------------

    /**
     * Produces the date the user has selected for the given preference, as a
     * calendar.
     *
     * @param preferences
     *          the SharedPreferences to get the date from
     * @param field
     *          the name of the preference to get the date from
     * @param defaultDate
     * the default date to use in case no preference is already saved
     * @return a Calendar that the user has selected
     */
    public static Calendar getDateFor(SharedPreferences preferences,
                                      String field, Date defaultDate)
    {
        String defaultString = defaultDate != null ? formatter().format(defaultDate) : "";
        String persisted     = preferences.getString(field, defaultString);

        Date date;
        if ("".equals(persisted)) {
            if (defaultDate == null) return null;
            date = defaultDate;
        } else {
            date = stringToDate(persisted);
        }

        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar;
    }

    //-------------------------------------------------------------------------

    /**
     * Produces the date the user has selected for the given preference, as a
     * calendar.
     *
     * @param preferences
     *          the SharedPreferences to get the date from
     * @param field
     *          the name of the preference to get the date from
     * @return a Calendar that the user has selected
     */
    public static Calendar getDateFor(SharedPreferences preferences, String field)
    {
        Date date = stringToDate(preferences.getString(field,
            defaultCalendarString()));
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        return calendar;
    }

    //-------------------------------------------------------------------------

    private static Date stringToDate(String dateString)
    {
        try {
            return formatter().parse(dateString);
        } catch (ParseException e) {
            return defaultCalendar().getTime();
        }
    }

    //-------------------------------------------------------------------------
    // Saved State
    //-------------------------------------------------------------------------

    private static class SavedState extends BaseSavedState
    {
        //---------------------------------------------------------------------
        // constructors
        //---------------------------------------------------------------------

        public SavedState(Parcel parcel)
        {
            super(parcel);
            dateValue = parcel.readString();
        }

        //---------------------------------------------------------------------

        public SavedState(Parcelable parcel)
        {
            super(parcel);
        }

        //---------------------------------------------------------------------
        // overrides
        //---------------------------------------------------------------------

        @Override
        public void writeToParcel(Parcel out, int flags)
        {
            super.writeToParcel(out, flags);
            out.writeString(dateValue);
        }

        //---------------------------------------------------------------------
        // fields
        //---------------------------------------------------------------------

        String dateValue;

        @SuppressWarnings("unused")
        public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }
}
